package cn.lingyangwl.agile.system.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.lingyangwl.agile.auth.AuthApi;
import cn.lingyangwl.agile.common.security.utils.SecurityUtils;
import cn.lingyangwl.agile.model.module.auth.LoginUser;
import cn.lingyangwl.agile.msg.push.api.MsgPushApi;
import cn.lingyangwl.agile.system.assembly.AccessKeyAssembly;
import cn.lingyangwl.agile.system.constants.CacheCons;
import cn.lingyangwl.agile.system.manager.AccessKeyManager;
import cn.lingyangwl.agile.system.mapper.AccessKeyMapper;
import cn.lingyangwl.agile.system.model.entity.AccessKey;
import cn.lingyangwl.agile.system.model.rqrs.accesskey.AccessKeyDetailsReq;
import cn.lingyangwl.agile.system.model.rqrs.accesskey.AccessKeyQuery;
import cn.lingyangwl.agile.system.model.rqrs.accesskey.AccessKeyResp;
import cn.lingyangwl.agile.system.model.rqrs.accesskey.AccessKeySaveReq;
import cn.lingyangwl.agile.system.service.AccessKeyService;
import cn.lingyangwl.framework.cache.core.service.CacheService;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * API访问密钥, 由于密钥比较重要, 这里只能是看自己的密钥, 管理员可以看所有的密钥
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class AccessKeyServiceImpl extends ServiceImpl<AccessKeyMapper, AccessKey>
        implements IService<AccessKey>, AccessKeyService {

    @Resource
    private AccessKeyManager accessSecretManager;
    @Resource
    private CacheService cacheService;
    @Resource
    private AccessKeyAssembly accessKeyAssembly;
    @Resource
    private MsgPushApi msgPushApi;
    @Resource
    private AuthApi authApi;

    /**
     * 分页查询 API访问密钥
     *
     * @return 返回分页数据
     */
    @Override
    public List<AccessKeyResp> listAccessKey(AccessKeyQuery query) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        LambdaQueryWrapper<AccessKey> queryWrapper = AccessKey.lqw()
                .eq(StringUtils.isNotEmpty(query.getSecretId()), AccessKey::getSecretId, query.getSecretId())
                .eq(!loginUser.getIsTenantOwner(), AccessKey::getUserId, SecurityUtils.getUserId())
                .eq(StringUtils.isNotEmpty(query.getStatus()), AccessKey::getStatus, query.getStatus())
                .orderByDesc(AccessKey::getCreatedAt);
        List<AccessKey> list = this.list(queryWrapper);
        boolean isVerify = needVerify();
        return list.stream().map(accessKeyAssembly::toAccessKeyResp).peek(e -> {
            e.setSecretKey("");
            e.setIsNeedVerify(isVerify);
        }).collect(Collectors.toList());
    }


    /**
     * 根据主键查询 API访问密钥
     *
     * @param id 主键
     * @return 返回记录，没有返回
     */
    @Override
    public AccessKeyResp getAccessKeyById(Long id, LoginUser loginUser) {
        LambdaQueryWrapper<AccessKey> lqw = AccessKey.lqw()
                .eq(Objects.nonNull(loginUser) && !loginUser.getIsTenantOwner(), AccessKey::getUserId, SecurityUtils.getUserId())
                .eq(AccessKey::getId, id);
        AccessKey entity = this.getOne(lqw);
        AccessKeyResp resp = accessKeyAssembly.toAccessKeyResp(entity);
        resp.setIsNeedVerify(needVerify());
        return resp;
    }

    /**
     * 保存 API访问密钥
     *
     * @return 返回影响行数
     */
    @Override
    public boolean saveAccessKey(AccessKeySaveReq rq) {
        String baseString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
        String secretId = RandomUtil.randomString(baseString, 36);
        String secretKey = RandomUtil.randomString(baseString, 36);

        AccessKey entity = new AccessKey();
        entity.setSecretId(secretId);
        entity.setName(rq.getName());
        entity.setSecretKey(secretKey);
        entity.setUserId(SecurityUtils.getUserId());
        return this.save(entity);
    }

    /**
     * 更新 API访问密钥
     *
     * @param rq 修改的记录
     * @return 返回影响行数
     */
    @Override
    public boolean updateAccessKeyById(AccessKeySaveReq rq) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        AccessKey entity = accessKeyAssembly.toAccessKey(rq);
        AccessKey record = this.getById(rq.getId());
        if (Objects.isNull(record)) {
            return false;
        }
        String cacheKey = String.format(CacheCons.ACCESS_KEY, record.getSecretId());

        LambdaQueryWrapper<AccessKey> lqw = AccessKey.lqw()
                .eq(!loginUser.getIsTenantOwner(), AccessKey::getUserId, SecurityUtils.getUserId())
                .eq(AccessKey::getId, rq.getId());
        boolean ret = this.update(entity, lqw);
        if (ret) {
            cacheService.opsForKey().delete(cacheKey);
        }
        return ret;
    }

    @Override
    public AccessKeyResp getAccessKeyBySecretId(String secretId) {
        LambdaQueryWrapper<AccessKey> lqw = AccessKey.lqw()
                .eq(AccessKey::getSecretId, secretId);
        return accessKeyAssembly.toAccessKeyResp(this.getOne(lqw));
    }

    /**
     * 通过id删除 API访问密钥
     *
     * @param id 删除的主键
     * @return 返回影响行数
     */
    @Override
    public boolean deleteAccessKey(Long id) {

        LoginUser loginUser = SecurityUtils.getLoginUser();
        LambdaQueryWrapper<AccessKey> queryWrapper = AccessKey.lqw()
                .eq(AccessKey::getId, id)
                .eq(!loginUser.getIsTenantOwner(), AccessKey::getUserId, SecurityUtils.getUserId());

        AccessKey record = this.getOne(queryWrapper);
        if (Objects.isNull(record)) {
            return false;
        }
        String cacheKey = String.format(CacheCons.ACCESS_KEY, record.getSecretId());
        cacheService.opsForKey().delete(cacheKey);

        return this.remove(queryWrapper);
    }

    /**
     * 通过ids删除 API访问密钥
     *
     * @param ids 删除的主键
     * @return 返回影响行数
     */
    @Override
    public boolean batchDeleteAccessKey(List<Long> ids) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        LambdaQueryWrapper<AccessKey> queryWrapper = AccessKey.lqw()
                .in(AccessKey::getId, ids)
                .eq(!loginUser.getIsTenantOwner(), AccessKey::getUserId, SecurityUtils.getUserId());

        List<AccessKey> records = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(records)) {
            return false;
        }
        records.stream().map(e -> String.format(CacheCons.ACCESS_KEY, e.getSecretId()))
                .forEach(cacheKey -> cacheService.opsForKey().delete(cacheKey));
        return this.remove(queryWrapper);
    }

    @Override
    public AccessKeyResp getAccessKeyBaseInfoById(Long id) {
        if (Objects.isNull(id)) {
            return null;
        }
        AccessKeyResp rs = this.getAccessKeyById(id, SecurityUtils.getLoginUser());
        if (Objects.isNull(rs)) {
            return null;
        }
        rs.setSecretKey("");
        return rs;
    }

    @Override
    public AccessKeyResp getAccessKeyDetails(AccessKeyDetailsReq req) {
        if (needVerify()) {
            // 验证码校验
            msgPushApi.checkAuthUserCaptcha(accessKeyAssembly.toCheckAuthUserVerifyMsgReq(req));

            String cacheKey = String.format(CacheCons.SHOW_ACCESS_KEY, SecurityUtils.getLoginUser().getLoginId());
            cacheService.opsForValue().setEx(cacheKey, "", CacheCons.SHOW_ACCESS_KEY_TIMEOUT, TimeUnit.SECONDS);
        }
        AccessKeyResp resp = this.getAccessKeyById(req.getId(), SecurityUtils.getLoginUser());
        resp.setIsNeedVerify(false);
        return resp;
    }

    /**
     * 是否需要验证
     * @return true 无需验证, 之前已经验证过了
     */
    private boolean needVerify() {
        String cacheKey = String.format(CacheCons.SHOW_ACCESS_KEY, SecurityUtils.getLoginUser().getLoginId());
        return !cacheService.opsForKey().hasKey(cacheKey);
    }
}
